/**
 * @file fs.c
 * @author LokLiang (lokliang@163.com)
 * @brief fs.h 的接口代码
 * @version 0.1
 * @date 2023-06-12
 *
 * @copyright Copyright (c) 2023
 *
 */

#include "components/fs.h"

#define SYS_LOG_DOMAIN "FS"
#define CONS_ABORT()
#include "sys_log.h"
#include "sys_init.h"
#include "sys_common.h"

#include <stdlib.h>
#include <errno.h>

static slist_t s_fs_list;
static sys_lock_t s_lock_hdl;
static char s_curr_dir[FS_PATH_NAME_LEN];

static fs_mount_t *_find_mp_by_fs_name(const char *fs_name);
static fs_mount_t *_parse_path(char abspath[FS_PATH_NAME_LEN], fs_path_t **ppath);
static int _readlink(char abspath[FS_PATH_NAME_LEN], fs_path_t *path);
static unsigned _dir_name(char *path_dst, fs_path_t *path_src, unsigned num);

static fs_mount_t *_find_mp_by_fs_name(const char *fs_name)
{
    fs_mount_t *mp = NULL;
    SLIST_FOR_EACH_CONTAINER(&s_fs_list, mp, node)
    {
        if (strcmp(fs_name, mp->fs_name) == 0)
        {
            return mp;
        }
    }
    return NULL;
}

static fs_mount_t *_parse_path(char abspath[FS_PATH_NAME_LEN], fs_path_t **ppath)
{
    if (_readlink(abspath, *ppath) < 2)
    {
        return NULL;
    }

    _dir_name(abspath, abspath, 1);
    fs_mount_t *mp = _find_mp_by_fs_name(abspath);
    int fs_len = strlen(abspath);
    abspath[fs_len] = '/';
    if (mp)
    {
        int volume_len = strlen(mp->volume_name);
        if (strncmp(mp->volume_name, &abspath[fs_len + 1], volume_len) != 0)
        {
            return NULL;
        }
        *ppath = &abspath[fs_len + 1];
    }
    return mp;
}

/**
 * @brief 解析出绝对路径
 *
 * @param abspath[out] 输出
 * @param path 被解析路径
 * @retval >=0 路径层数;
 * @retval < 0 失败;
 */
static int _readlink(char abspath[FS_PATH_NAME_LEN], fs_path_t *path)
{
    if (path == NULL || *path == '\0' || *path == '/')
    {
        *abspath = '\0';
    }
    else
    {
        strcpy(abspath, s_curr_dir);
        strncat(abspath, "/", FS_PATH_NAME_LEN - 1);
    }
    strncat(abspath, path, FS_PATH_NAME_LEN - 1);
    _dir_name(abspath, abspath, FS_PATH_NAME_LEN);
    int lev = -1;
    char *dst = abspath;
    path = abspath;
    while (1)
    {
        if (*path == '.')
        {
            // ".?"
            if (lev < 0)
            {
                abspath[0] = '\0';
                return -1;
            }

            path++;
            if (*path == '.')
            {
                // "..?"
                if (lev < 1)
                {
                    abspath[0] = '\0';
                    return -1;
                }

                path++;
                if (*path == '/')
                {
                    // "../"
                    lev--;
                    _dir_name(abspath, abspath, lev);
                    dst = &abspath[strlen(abspath)];
                    *dst++ = *path++;
                }
                else if (*path == '\0')
                {
                    // "..\0"
                    lev--;
                    break;
                }
                else if (*path == '.')
                {
                    // "..."
                    abspath[0] = '\0';
                    return -1;
                }
                else
                {
                    // "..?"
                    *dst++ = '.';
                    *dst++ = '.';
                }
            }
            else if (*path == '/')
            {
                // "./"
                path++;
            }
            else if (*path == '\0')
            {
                // ".\0"
                break;
            }
            else
            {
                // ".?"
                *dst++ = '.';
            }
        }
        else
        {
            if (*path == '/')
            {
                lev++;
            }
            else if (*path == '\0')
            {
                lev++;
                *dst = '\0';
                break;
            }
            *dst++ = *path++;
        }
    }

    if (*abspath != '/')
    {
        return -1;
    }
    else
    {
        _dir_name(abspath, abspath, lev);
        return lev;
    }
}

/**
 * @brief 1.去除多余的'/'; 2.分离路径名
 *
 * 例如: _dir_name(path, "/a//b/c", 2)
 * path = "/a/b"
 * 返回 2
 *
 * @param path_dst[out] 输出
 * @param path_src 来源路径
 * @param num 以 '/' 为分隔符，取多少层目录
 * @return int 实际取得的目录层数
 */
static unsigned _dir_name(char *path_dst, fs_path_t *path_src, unsigned num)
{
    const char sp = '/';

    if (*path_src == sp)
        *path_dst++ = *path_src++;

    for (int i = 0; i < num; i++)
    {
        while (*path_src == sp && *path_src != '\0')
            path_src++;

        if (*path_src == '\0')
        {
            if (i)
                path_dst--;
            num = i;
            break;
        }

        while (*path_src != sp && *path_src != '\0')
            *path_dst++ = *path_src++;

        if (i + 1 < num)
        {
            if (*path_src == '\0')
            {
                num = i + 1;
                break;
            }

            *path_dst++ = *path_src++;
        }
    }
    *path_dst = '\0';
    return num;
}

/**
 * @brief 添加一种文件系统的接口，关联到 fs_name
 *
 * @param fs_name 文件系统的关联名称。如 "/ff"
 * @param api 文件系统的操作接口
 * @retval 0 成功;
 * @retval < 0 失败;
 */
int fs_regist(const char *fs_name, const fs_api_t *api)
{
    if (fs_name == NULL || fs_name[0] != '/')
    {
        SYS_LOG_ERR("the volume name '%s' format incorrect", fs_name);
        return -EINVAL;
    }
    if (strlen(fs_name) >= sizeof(((fs_mount_t *)0)->fs_name))
    {
        SYS_LOG_ERR("the volume name '%s' was more then %d bytes", fs_name, sizeof(((fs_mount_t *)0)->fs_name) - 1);
        return -EINVAL;
    }
    sys_lock(&s_lock_hdl, SYS_LOG_FOREVER);

    fs_mount_t *mp = _find_mp_by_fs_name(fs_name);
    if (mp)
    {
        sys_unlock(&s_lock_hdl);
        SYS_LOG_ERR("repeated regist volume name '%s'", fs_name);
        return -EINVAL;
    }

    mp = calloc(1, sizeof(fs_mount_t));
    if (mp == NULL)
    {
        sys_unlock(&s_lock_hdl);
        SYS_LOG_ERR("insufficient memory");
        return -ENOMEM;
    }

    mp->api = api;
    strcpy(mp->fs_name, fs_name);
    slist_init_node(&mp->node);
    slist_insert_tail(&s_fs_list, &mp->node);

    sys_unlock(&s_lock_hdl);
    return 0;
}

/**
 * @brief iibc文件系统的接口与 fs_name 的关联
 *
 * @param fs_name 文件系统的关联名称
 * @retval 0 成功;
 * @retval < 0 失败;
 */
int fs_unregist(const char *fs_name)
{
    sys_lock(&s_lock_hdl, SYS_LOG_FOREVER);

    fs_mount_t *mp = NULL;
    SLIST_FOR_EACH_CONTAINER(&s_fs_list, mp, node)
    {
        if (strcmp(fs_name, mp->fs_name) == 0)
        {
            if (mp->cfg.mount_flag)
            {
                mp->api->unmount(mp, fs_name);
            }
            slist_remove(&s_fs_list, &mp->node);
            free(mp);
            break;
        }
    }

    sys_unlock(&s_lock_hdl);

    if (mp)
    {
        return 0;
    }
    else
    {
        SYS_LOG_ERR("can not find volume name '%s'", fs_name);
        return -ENXIO;
    }
}

/**
 * @brief 获取可用于 fs_mount() 挂载的卷名
 *
 * @param fs_name 来自 fs_regist() 所注册的文件系统关联名
 * @param num 范围 0..n
 * @return const char* volume_name
 */
const char *getmnt(const char *fs_name, uint8_t num)
{
    char name[FS_VOLUME_NAME_LEN + 16];
    SYS_SNPRINT(name, sizeof(name), "disk:%s:", fs_name);
    const char *ret = device_search(name, num);
    if (ret)
    {
        return &ret[strlen(name)];
    }
    return NULL;
}

/**
 * @brief 执行创建文件系统
 *
 * @param fs_name 由 fs_regist() 注册过的文件系统关联名
 * @param volume_name 指定卷名
 * @retval 0 成功;
 * @retval < 0 失败;
 */
int fs_mkfs(const char *fs_name, const char *volume_name)
{
    fs_mount_t *mp = _find_mp_by_fs_name(fs_name);
    if (mp == NULL)
    {
        SYS_LOG_ERR("unregistered file system '%s'", fs_name);
        return -ENOENT;
    }

    if (mp->api == NULL || mp->api->mkfs == NULL)
    {
        SYS_LOG_ERR("never installed api 'mkfs'");
        return -ENOTSUP;
    }

    int ret = mp->api->mkfs(mp, volume_name);
    if (ret < 0)
    {
        SYS_LOG_ERR("mkfs error (%d)", ret);
        return ret;
    }

    return 0;
}

/**
 * @brief 执行挂载
 *
 * @param fs_name 来自 fs_regist() 所注册的文件系统关联名
 * @param volume_name 指定卷名
 * @retval 0 成功;
 * @retval < 0 失败;
 */
int fs_mount(const char *fs_name, const char *volume_name)
{
    fs_mount_t *mp = _find_mp_by_fs_name(fs_name);
    if (mp == NULL)
    {
        SYS_LOG_ERR("unregistered file system '%s'", fs_name);
        return -ENOENT;
    }

    if (mp->api == NULL || mp->api->mount == NULL)
    {
        SYS_LOG_ERR("never installed api 'mount'");
        return -ENOTSUP;
    }

    int ret = mp->api->mount(mp, volume_name);
    if (ret < 0)
    {
        SYS_LOG_ERR("mount error (%d)", ret);
        return ret;
    }

    strncpy(mp->volume_name, volume_name, sizeof(mp->volume_name) - 1);

    return 0;
}

/**
 * @brief 取消挂载
 *
 * @param fs_name 来自 fs_regist() 所注册的文件系统关联名
 * @param volume_name 指定卷名
 * @retval 0 成功;
 * @retval < 0 失败;
 */
int fs_unmount(const char *fs_name, const char *volume_name)
{
    fs_mount_t *mp = _find_mp_by_fs_name(fs_name);
    if (mp == NULL)
    {
        SYS_LOG_ERR("unregistered file system '%s'", fs_name);
        return -ENOENT;
    }

    if (mp->api == NULL || mp->api->unmount == NULL)
    {
        SYS_LOG_ERR("never installed api 'unmount'");
        return -ENOTSUP;
    }

    int flag = 1;
    if (mp->api->getcwd)
    {
        char abspath1[FS_PATH_NAME_LEN];
        strcpy(abspath1, fs_name);
        int len = strlen(abspath1);
        abspath1[len++] = '/';
        mp->api->getcwd(mp, &abspath1[len], sizeof(abspath1) - len);

        char abspath2[FS_VOLUME_NAME_LEN + FS_FILE_NAME_LEN];
        _dir_name(abspath2, s_curr_dir, 2);

        flag = strncmp(abspath1, abspath2, strlen(abspath2));
    }

    int ret = mp->api->unmount(mp, volume_name);
    if (ret < 0)
    {
        SYS_LOG_ERR("unmount error (%d)", ret);
        return ret;
    }

    if (flag == 0)
    {
        strcpy(s_curr_dir, "/");
    }

    return 0;
}

/**
 * @brief 打开或创建一个文件
 *
 * @param fp 文件对象
 * @param path 被打开或创建的路径（绝对路径）
 * @param flags 打开模式
 *   - @c FS_O_READ 允许读
 *   - @c FS_O_WRITE 允许写
 *   - @c FS_O_CREATE 如果文件不存在则创建
 *   - @c FS_O_APPEND 当执行写入前，其指针总是移动到结尾
 * @retval 0 成功;
 * @retval < 0 失败;
 */
int fs_open(fs_file_t *fp, fs_path_t *path, uint8_t flags)
{
    if (fp->mp != NULL)
    {
        SYS_LOG_WRN("file has been opened");
        return -EBUSY;
    }

    char abspath[FS_PATH_NAME_LEN];
    fp->mp = _parse_path(abspath, &path);
    if (fp->mp == NULL)
    {
        SYS_LOG_ERR("invalid path '%s'", path);
        return -EINVAL;
    }

    if (fp->mp->api == NULL || fp->mp->api->open == NULL)
    {
        SYS_LOG_ERR("never installed api 'open'");
        fp->mp = NULL;
        return -ENOTSUP;
    }

    fp->file_hdl = NULL;
    int ret = fp->mp->api->open(fp, path, flags);
    if (ret < 0)
    {
        SYS_LOG_ERR("open error (%d)", ret);
        fp->mp = NULL;
        return ret;
    }

    fp->flags = flags;

    return 0;
}

/**
 * @brief 关闭由 fs_open() 打开的文件
 *
 * @param fp 由 fs_open() 设置的对象
 * @retval 0 成功;
 * @retval < 0 失败;
 */
int fs_close(fs_file_t *fp)
{
    if (fp->mp == NULL)
    {
        return 0;
    }

    if (fp->mp->api == NULL || fp->mp->api->close == NULL)
    {
        SYS_LOG_ERR("never installed api 'close'");
        return -ENOTSUP;
    }

    int ret = fp->mp->api->close(fp);
    if (ret < 0)
    {
        SYS_LOG_ERR("close error (%d)", ret);
        return ret;
    }

    fp->mp = NULL;
    fp->file_hdl = NULL;

    return 0;
}

/**
 * @brief 读取文件数据
 *
 * @param fp 由 fs_open() 设置的对象
 * @param dest 读取缓存
 * @param size 字节
 * @retval >= 0 成功读取的字节数
 * @retval < 0 失败;
 */
int fs_read(fs_file_t *fp, void *dest, size_t size)
{
    if (fp->mp == NULL)
    {
        SYS_LOG_ERR("file never been opened");
        return -EBADF;
    }

    if (fp->mp->api == NULL || fp->mp->api->read == NULL)
    {
        SYS_LOG_ERR("never installed api 'read'");
        return -ENOTSUP;
    }

    int ret = fp->mp->api->read(fp, dest, size);
    if (ret < 0)
    {
        SYS_LOG_ERR("read error (%d)", ret);
    }

    return ret;
}

/**
 * @brief 写入数据到文件
 *
 * @param fp 由 fs_open() 设置的对象
 * @param src 缓存
 * @param size 字节
 * @retval >= 0 成功写入的字节数
 * @retval < 0 操作失败
 */
int fs_write(fs_file_t *fp, const void *src, size_t size)
{
    if (fp->mp == NULL)
    {
        SYS_LOG_ERR("file never been opened");
        return -EBADF;
    }

    if (fp->mp->api == NULL || fp->mp->api->write == NULL)
    {
        SYS_LOG_ERR("never installed api 'write'");
        return -ENOTSUP;
    }

    int ret = fp->mp->api->write(fp, src, size);
    if (ret < 0)
    {
        SYS_LOG_ERR("write error (%d)", ret);
    }

    return ret;
}

/**
 * @brief 设置当前的读写指针
 *
 * @param fp 由 fs_open() 设置的对象
 * @param offset 偏移量
 * @param whence 偏移量的参考位置
 *   - @c FS_SEEK_SET 从文件的起始位置
 *   - @c FS_SEEK_CUR 从文件的当前位置
 *   - @c FS_SEEK_END 从文件的结束位置
 * @retval > 0 （可选）成功，文件的偏移量对应所在的物理扇区号;
 * @retval 0 成功;
 * @retval < 0 失败;
 */
int fs_lseek(fs_file_t *fp, int offset, uint8_t whence)
{
    if (fp->mp == NULL)
    {
        SYS_LOG_ERR("file never been opened");
        return -EBADF;
    }

    if (fp->mp->api == NULL || fp->mp->api->lseek == NULL)
    {
        SYS_LOG_ERR("never installed api 'lseek'");
        return -ENOTSUP;
    }

    int ret = fp->mp->api->lseek(fp, offset, whence);
    if (ret < 0)
    {
        SYS_LOG_ERR("lseek error (%d)", ret);
    }
    return ret;
}

/**
 * @brief 按指定的长度截断或扩展文件大小。
 *
 * @param fp 由 fs_open() 设置的对象
 * @param length 字节
 * @retval 0 成功;
 * @retval < 0 失败;
 */
int fs_truncate(fs_file_t *fp, int length)
{
    if (fp->mp == NULL)
    {
        SYS_LOG_ERR("file never been opened");
        return -EBADF;
    }

    if (fp->mp->api == NULL || fp->mp->api->truncate == NULL)
    {
        SYS_LOG_ERR("never installed api 'truncate'");
        return -ENOTSUP;
    }

    int ret = fp->mp->api->truncate(fp, length);
    if (ret < 0)
    {
        SYS_LOG_ERR("truncate error (%d)", ret);
        return ret;
    }

    return 0;
}

/**
 * @brief 立即更新操作缓存到存储器中
 *
 * @param fp 由 fs_open() 设置的对象
 * @retval 0 成功;
 * @retval < 0 失败;
 */
int fs_sync(fs_file_t *fp)
{
    if (fp->mp == NULL)
    {
        SYS_LOG_ERR("file never been opened");
        return -EBADF;
    }

    if (fp->mp->api == NULL || fp->mp->api->sync == NULL)
    {
        SYS_LOG_ERR("never installed api 'sync'");
        return -ENOTSUP;
    }

    int ret = fp->mp->api->sync(fp);
    if (ret < 0)
    {
        SYS_LOG_ERR("sync error (%d)", ret);
        return ret;
    }

    return 0;
}

/**
 * @brief 获取文件当前的读写指针位置
 *
 * @param fp 由 fs_open() 设置的对象
 * @retval >= 0 当前的读写指针位置
 * @retval < 0 操作失败
 */
int fs_tell(fs_file_t *fp)
{
    if (fp->mp == NULL)
    {
        SYS_LOG_ERR("file never been opened");
        return -EBADF;
    }

    if (fp->mp->api == NULL || fp->mp->api->tell == NULL)
    {
        SYS_LOG_ERR("never installed api 'tell'");
        return -ENOTSUP;
    }

    int ret = fp->mp->api->tell(fp);
    if (ret < 0)
    {
        SYS_LOG_ERR("tell error (%d)", ret);
    }

    return ret;
}

/**
 * @brief 打开一个目录
 *
 * @param dp 目录对象
 * @param path 被打开的路径（绝对路径）
 * @retval 0 成功;
 * @retval < 0 失败;
 */
int fs_opendir(fs_dir_t *dp, fs_path_t *path)
{
    if (dp->mp != NULL || dp->dir_hdl != NULL)
    {
        SYS_LOG_WRN("directory has been opened");
        return -EBUSY;
    }

    if (strcmp(path, "/") != 0)
    {
        char abspath[FS_PATH_NAME_LEN];
        dp->mp = _parse_path(abspath, &path);
        if (dp->mp == NULL)
        {
            SYS_LOG_ERR("invalid path '%s'", path);
            return -EINVAL;
        }

        if (dp->mp->api == NULL || dp->mp->api->opendir == NULL)
        {
            SYS_LOG_ERR("never installed api 'opendir'");
            dp->mp = NULL;
            return -ENOTSUP;
        }

        dp->dir_hdl = NULL;
        int ret = dp->mp->api->opendir(dp, path);
        if (ret < 0)
        {
            dp->mp = NULL;
            SYS_LOG_ERR("opendir error (%d)", ret);
            return ret;
        }
    }
    else
    {
        dp->mp = NULL;
        dp->dir_hdl = slist_peek_head(&s_fs_list);
    }

    return 0;
}

/**
 * @brief 关闭一个已打开的目录
 *
 * @param dp 由 fs_opendir() 设置的对象
 * @retval 0 成功;
 * @retval < 0 失败;
 */
int fs_closedir(fs_dir_t *dp)
{
    if (dp->mp == NULL)
    {
        return 0;
    }

    if (dp->mp->api == NULL || dp->mp->api->closedir == NULL)
    {
        SYS_LOG_ERR("never installed api 'closedir'");
        return -ENOTSUP;
    }

    int ret = dp->mp->api->closedir(dp);
    if (ret < 0)
    {
        SYS_LOG_ERR("closedir error (%d)", ret);
        return ret;
    }

    dp->mp = NULL;
    dp->dir_hdl = NULL;

    return 0;
}

/**
 * @brief 读取目录下的下一条目。
 * 当 info->name[0] == '\0' 时表示没有下一条目。
 *
 * @param dp 由 fs_opendir() 设置的对象
 * @retval 0 成功;
 * @retval < 0 失败;
 */
int fs_readdir(fs_dir_t *dp, fs_file_info_t *info)
{
    if (dp->mp)
    {
        if (dp->mp->api == NULL || dp->mp->api->readdir == NULL)
        {
            SYS_LOG_ERR("never installed api 'readdir'");
            return -ENOTSUP;
        }

        int ret = dp->mp->api->readdir(dp, info);
        if (ret < 0)
        {
            SYS_LOG_ERR("readdir error (%d)", ret);
        }

        return ret;
    }

    /* 根目录 */
    if (dp->dir_hdl == NULL)
    {
        info->name[0] = 0;
        return 0;
    }

    sys_lock(&s_lock_hdl, SYS_LOG_FOREVER);
    int ret = -ENOENT;
    fs_mount_t *mp = NULL;
    slist_node_t *node = NULL;
    SLIST_FOR_EACH_CONTAINER(&s_fs_list, mp, node)
    {
        if (node == dp->dir_hdl)
        {
            strncpy(info->name, &mp->fs_name[1], sizeof(info->name) - 1);
            info->type = FS_INFO_TYPE_DIR;
            info->fsize = 0;
            dp->dir_hdl = slist_peek_next(dp->dir_hdl);
            ret = 0;
            break;
        }
    }
    sys_unlock(&s_lock_hdl);
    return ret;
}

/**
 * @brief 更改当前工作路径
 *
 * @param path 目录的路径（绝对路径）
 * @retval 0 成功;
 * @retval < 0 失败;
 */
int fs_chdir(fs_path_t *path)
{
    char abspath[FS_PATH_NAME_LEN];
    fs_mount_t *mp = _parse_path(abspath, &path);
    if (mp == NULL)
    {
        SYS_LOG_ERR("invalid path '%s'", path);
        return -EINVAL;
    }

    if (mp->api == NULL || mp->api->chdir == NULL)
    {
        SYS_LOG_ERR("never installed api 'chdir'");
        return -ENOTSUP;
    }

    int ret = mp->api->chdir(mp, path);
    if (ret < 0)
    {
        SYS_LOG_ERR("chdir error (%d)", ret);
        return ret;
    }

    if (mp->api->getcwd)
    {
        _dir_name(abspath, abspath, 1);
        int len = strlen(abspath);
        abspath[len++] = '/';
        mp->api->getcwd(mp, &abspath[len], sizeof(abspath) - len);
        _dir_name(abspath, abspath, sizeof(abspath));
    }
    strncpy(s_curr_dir, abspath, sizeof(s_curr_dir) - 1);

    return 0;
}

/**
 * @brief 当前当前的工作路径
 *
 * @param path[out] 输出
 * @param len path 的长度（字节）
 * @retval 0 成功;
 * @retval < 0 失败;
 */
int fs_getcwd(char *path, int len)
{
    strncpy(path, s_curr_dir, len - 1);
    return 0;
}

/**
 * @brief 创建一个目录（支持递归创建）
 *
 * @param path 目录的路径（绝对路径）
 * @retval 0 成功;
 * @retval < 0 失败;
 */
int fs_mkdir(fs_path_t *path)
{
    char abspath[FS_PATH_NAME_LEN];
    fs_mount_t *mp = _parse_path(abspath, &path);
    if (mp == NULL)
    {
        SYS_LOG_ERR("invalid path '%s'", path);
        return -EINVAL;
    }

    if (mp->api == NULL || mp->api->mkdir == NULL)
    {
        SYS_LOG_ERR("never installed api 'mkdir'");
        return -ENOTSUP;
    }

    if (mp->api->opendir != NULL && mp->api->closedir != NULL)
    {
        for (int i = 2;; i++)
        {
            char dir_str[FS_PATH_NAME_LEN];
            int lev = _dir_name(dir_str, path, i);
            if (i != lev)
            {
                if (lev < 2)
                {
                    return -EINVAL;
                }
                break;
            }

            fs_dir_t dir;
            memset(&dir, 0, sizeof(dir));
            if (mp->api->opendir(&dir, dir_str) == 0)
            {
                mp->api->closedir(&dir);
            }
            else
            {
                int ret = mp->api->mkdir(mp, dir_str);
                if (ret < 0)
                {
                    SYS_LOG_ERR("mkdir error (%d)", ret);
                    return ret;
                }
            }
        }
    }
    else
    {
        int ret = mp->api->mkdir(mp, path);
        if (ret < 0)
        {
            SYS_LOG_ERR("mkdir error (%d)", ret);
            return ret;
        }
    }

    return 0;
}

/**
 * @brief 删除一个文件或目录
 *
 * @param path 路径（绝对路径）
 * @retval 0 成功;
 * @retval < 0 失败;
 */
int fs_unlink(fs_path_t *path)
{
    char abspath[FS_PATH_NAME_LEN];
    fs_mount_t *mp = _parse_path(abspath, &path);
    if (mp == NULL)
    {
        SYS_LOG_ERR("invalid path '%s'", path);
        return -EINVAL;
    }

    if (mp->api == NULL || mp->api->unlink == NULL)
    {
        SYS_LOG_ERR("never installed api 'unlink'");
        return -ENOTSUP;
    }

    int ret = mp->api->unlink(mp, path);
    if (ret < 0)
    {
        SYS_LOG_ERR("unlink error (%d)", ret);
        return ret;
    }

    return 0;
}

/**
 * @brief 重命令文件或目录。如果目标文件或目录已存在，则将被覆盖。注意不能跨文件系统操作
 *
 * @param from 旧文件或目录名（绝对路径）
 * @param to 新文件或目录名（绝对路径）
 * @retval 0 成功;
 * @retval < 0 失败;
 */
int fs_rename(fs_path_t *from, fs_path_t *to)
{
    char abspath_from[FS_PATH_NAME_LEN];
    fs_mount_t *mp_from = _parse_path(abspath_from, &from);
    if (mp_from == NULL)
    {
        SYS_LOG_ERR("invalid from path '%s'", from);
        return -EINVAL;
    }
    char abspath_to[FS_PATH_NAME_LEN];
    fs_mount_t *mp_to = _parse_path(abspath_to, &to);
    if (mp_to == NULL)
    {
        SYS_LOG_ERR("invalid to path '%s'", to);
        return -EINVAL;
    }
    if (mp_from->api != mp_to->api)
    {
        SYS_LOG_ERR("mount point not same!!");
        return -EINVAL;
    }

    if (mp_to->api == NULL || mp_to->api->rename == NULL)
    {
        SYS_LOG_ERR("never installed api 'rename'");
        return -ENOTSUP;
    }

    int ret = mp_to->api->rename(mp_to, from, to);
    if (ret < 0)
    {
        SYS_LOG_ERR("rename error (%d)", ret);
        return ret;
    }

    return 0;
}

/**
 * @brief 获取文件或目录的状态
 *
 * @param path 文件或目录的路径（绝对路径）
 * @param info 缓存
 * @retval 0 成功;
 * @retval < 0 失败;
 */
int fs_stat(fs_path_t *path, fs_file_info_t *info)
{
    char abspath[FS_PATH_NAME_LEN];
    fs_mount_t *mp = _parse_path(abspath, &path);
    if (mp == NULL)
    {
        SYS_LOG_ERR("invalid path '%s'", path);
        return -EINVAL;
    }

    if (mp->api == NULL || mp->api->stat == NULL)
    {
        SYS_LOG_ERR("never installed api 'stat'");
        return -ENOTSUP;
    }

    int ret = mp->api->stat(mp, path, info);
    if (ret < 0)
    {
        SYS_LOG_ERR("stat error (%d)", ret);
        return ret;
    }

    return 0;
}

/**
 * @brief 获取文件系统中的容量信息
 *
 * @param fs_name 由 fs_regist() 注册过的文件系统关联名
 * @param volume_name 指定卷名
 * @param stat 缓存
 * @retval 0 成功;
 * @retval < 0 失败;
 */
int fs_statvfs(const char *fs_name, const char *volume_name, fs_statvfs_t *stat)
{
    fs_mount_t *mp = _find_mp_by_fs_name(fs_name);
    if (mp == NULL)
    {
        SYS_LOG_ERR("unregistered file system '%s'", fs_name);
        return -ENOENT;
    }

    if (mp->api == NULL || mp->api->statvfs == NULL)
    {
        SYS_LOG_ERR("never installed api 'statvfs'");
        return -ENOTSUP;
    }

    int ret = mp->api->statvfs(mp, stat, volume_name);
    if (ret < 0)
    {
        SYS_LOG_ERR("statvfs error (%d)", ret);
        return ret;
    }

    return 0;
}
